home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Pascal Super Library
/
Pascal Super Library (CW International)(1997).bin
/
LIBRARY
/
SWDOS12
/
SWDOS.ENG
< prev
next >
Wrap
Text File
|
1995-02-10
|
26KB
|
645 lines
Unit SWDOS
Various tools for DOS calls from Turbo Pascal
Copyright (æ) by SoftWeyr, 1994
INTRODUCTION
This unit contains a large collection of various small, but
useful tools for deal with DOS from Turbo Pascal program. It is
not replacement for DOS unit, but addition to it.
SWDOS must work in any version of TP higher than 4.0. But I
hadn't tried. Some functions cannot work in versions prior 6.0.
Most of functions are compatible with DOS 3.+, but some is
specific for DOS 5.+
REVISION HISTORY
July 1994 - first version created.
September 1994 - small bugs fixed, file name handling added.
November 1994 - Incomparibility with TP 7.0 fixed.
TEXTSEEK and TEXTOPS routines added.
PROCEDURES AND FUNCTIONS
I. FILE OPERATION
Procedures and functions in this chapter generally
duplicate corresponding routines from DOS.TPU, with one little
difference - they expect not file variable as a parameter, but
string, contains filename or file handle.
Why? If you work with TDosStream objects or its
descendants, it seems be to expansive - link all file I/O
library simply for file deletion or renaming. Borland includes
PrintStr procedure to avoid linking of file I/O library in
Turbo Vision program.
Also, this procedures never causes a Runtime Error,
regardless of compilation flags. In case of Error they sets
value of DOSError variable, declared in DOS unit.
I.1 Procedure RenameFile(OldName,NewName:String);
Renames file
I.2 Procedure EraseFile(FileName:String);
Erases file.
I.3 Procedure SetFileAttr(FileName:String;Attr:Word);
Changes attributes. Note, that this procedure is applicable
to directories.
I.4 Function GetFileAttr(FileName:String):Word;
Returns attributes. Note, not in var parameter as GetFAttr,
but as a function result. That allows you to check
attributes without declaring two additional variables (one of
type file and second of type word)
All functions above takes no care, is file opened or
closed. Situation with file creation time is more complicated.
I.5. Procedure SetFileTime(FileName:String;DateTime:LongInt);
I.6. Procedure GetFileTime(FileName : String; var DateTime
: Longint);
This procedures works ONLY if file CLOSED.
If file is open, its handle must exist and be stored anywhere.
For instance in Handle field if TDosStream object.
In this case use:
I.7. Procedure SetHandleTime(Handle:Word;DateTime:LongInt);
I.8. Procedure GetHandleTime(Handle:Word;var DateTime:Longint);
(Really xxxFileTime takes following three steps:
1. Open file for reading (Access code 0)
2. Call xxxHandleTime
3. Close file)
II. Control of file count
May be, you'd already have Runtime
Error 004 - to many open files. Usially it means that you
somewhere forget to close files. But sometimes you really need
a lot of files. You press F1 and read:
DOS never allows more than 15 open files per
process.
If you get this error with less than 15 open
files, the CONFIG.SYS file might not include a
FILES=xx entry (or xx might specify too few
files).
Increase the number xx to some suitable value,
such as 20.
But you have FILES=40 or more in your CONFIG.SYS and
applications like Paradox or FoxPro can do something with it.
Really quote from help above is false. There is a special
DOS function, that let you request as many files as you need.
Up to 255. I had read that you can request more files than
declared in CONFIG.SYS, but it seems to be a lie too.
At least you can obtain as many as you write in CONFIG.SYS,
if you call procedure:
II.1. MaxFiles(Count:Integer)
Where Count - number of files, you want plus five standard
files:
STDIN - corresponds with INPUT, if you don't use CRT or
someting simular
STDOUT - OUTPUT with same restrictions
STDERR - Console output file which cannot be redirecrd, in
difference to STDOUT
STDPRN - Default printer. Has no relations with pascal LST.
STDAUX - Default COM port. Usially unusable.
All names above aren't DOS file or device name. They are common
symbolic constants for predefined file handles from 0 to 4.
(these are declared in SWDOS.TPU to)
You can even close this handles to obtain some more files for
your own affairs.
But if you write command-line controlled program, you can
find some usage for STDERR.
II.2. Procedure CloseHandle(Hadle:Word);
Allows you to close a file handle. May be useful, if you want
free up standard handles or if you want to close DOS file
without deallocating corresponding object or buffer.
III. Standard I/O
If you write program which uses STDOut, you may want to
know, does it write to screen or somewhere else (to disk file,
for example). If you doubt, redirect TPC output to file and
look into it with text editor (but not IDE)
III.1 Function Redirected(var F:TExt):Boolean;
Allows to do this. It returns True, if output comes to printer
or file, and input from file.
It is usable only for files, which are assigned to STDOUT or
STDIN (You can do this by Assign(F,'') and Rewrite or Reset
respectively)
By default INPUT = STDIN and OUTPUT=STDOUT, but if in first
line of program you have uses CRT (TPCRT,OPCRT) or this units
are used at list of one of linked units, this files have no
relations with any DOS devices. But at least in TP 6.0 this
function works correctly with CRT, becouse ASSIGNCRT procedure
doesn't alter Handle field of TextRec structure.
If you write commandline-controlled program with
rudimentary interactivity like
File Already Exist.
Overwrite?(Y/N), STDERR standard file can be useful. Sometimes
you want to use STDPRN and even STDAUX. There are two
procedures for use them:
III.2. Procedure OpenSTD(var F:Text;Device:Word);
III.3. Procedure StdWrite(Device:Word;S:String);
where Device - one of standard handles. Of couse
DevWrite(STDIN,...) is meaningless. You wouldn't recieve
anything except error.
In OpenDevice apart five constants above you can use
constant STDAUXin, which means that you want to open STDAUX for
reading. Due to all other standard devices are either read-only
or write-only, I include call to Reset or Rewrite into OpenSTD
You aren't need to close this files.
And something more for STDOUT. Sometimes it is good, if output
is visible on screen, even if it is redirected.
III.4 Procedure Tee(var F:Text);
Allows to do this. It is applicable for files which are
assigned via AssignCRT or AssignDevice of Turbo Vision TextView
unit, and sends to STDOUT any character, which was written to
this file (if STDOUT is not redirected, you will see all output
twice. I recommend you following sequence
Uses Crt;
...
if Redirected(Output) then Tee(Output);
or (for Turbo Vision);
var T:PTerminal;
.......
AssignDevice(Output,T);
Rewrite(T);
if Redirected(Output) then Tee(Output);
Sometimes it is good to pause output after each screen of
information. But it is too boring - search through your program
for each Writeln and count them.
III.5 Procedure SetPagingMode(var F :Text; H :Integer; Message
:String);
Helps you in this case. It would display a given Message after
each H lines and wait for Y or N pressed. If Y was pressed, it
would show next screen, and if N, it would place value 100 -
disk write error into IOResult. Compile your program with $I-
and do not forget to check IOResult, if you don't really want
your program halt after negative answer.
III.6. Procedure EndPagingMode(var F:Text);
Finishes Paging mode for specified file.
Do not apply SetPagingMode to more than one file at same time!
Reading from keyboard is reading from STDIN. Borland
preferes to do it via BIOS, and places corresponding functions
into CRT units. I think, that is out of logic, becouse CRT is
Catode-Ray Tube i.e phisical screen, not even a console.
In C language corresponding module is much more logically named
CONIO - console input/output.
I've placed in SWDOS analogues of this functions, which works
via DOS and implemented as inline, becouse they a almost
shorter than far call.
III.7 Function DOSReadKey:Char;
Full analogue of ReadKey
III.8 Function ReadKeyWithEcho:Char;
No comments
III.9 Function DOSKeyPressed:Boolean;
Analogue of KeyPressed (longest function of these three)
And some more about reading. Sometimes useful to get string
from file as function result, for example to uppercase it
before storing anywhere.
III.10 Function GetStr(var F:Text):String;
Simply perform Readln(F,String_variable) and return result.
IV.Dealing with Environment ¿ PSP
I hope, you know, what is DOS Environment. It is that very
place, where COMSPEC, PATH and other simular information is
stored.
Becouse each program have its own copy of environment, you
usially wish to modify it, only if you want to start child
process.
For instance many applications add its name to prompt,
when they calls DOS Shell.
But there are some utilities which can return some
information in envronment variables. How?
There is a pointer to Environment block in PSP - program
segment prefix, which every program has. In this prefix also
located command line and some other information.
Among this information any process, except COMMAND.COM (not
only root, but any copy) has a pointer to PSP of its parent
process. Therefore, environment of parent is accessible, and if
you change it, this changes would be visible after termination
of your process.
You can also access Environment of first(root) copy of
COMMAND.COM. It is nearly unusable for users of Norton
Commander and other such shells, becouse program, started from
shell, inherits Environment of Shell, not of root COMMAND.COM.
But root Environment always have more free space than any
other, becouse it size is exactly that value, which you'd
specified in CONFIG.SYS SHELL command, but any other
environment is truncates on nearest paragraph after end of last
variable. (It is reason, why complicated batch files cannot
work under NC)
However, if you can access PSP of parent, you can
also look into another its field, like command line.
Once I've had used such strange way to pass information from
child to parent.
All functions, of SWDOS, which deal with Environment
and PSP, access "current" PSP and Environment. It is not
always Environment and PSP of current program. It can be set by
following procedures:
IV.1 AccessCurrentEnv
Make environment of your own program current (default)
IV.2 AccessParentEnv
Make environment of parent current
IV.3 AccessRootEnv - ñ« ¬«α¡Ñó«ú«.
Make environment of root COMMAND.COM current
However all environment related procedures of DOS and System
units (ParamStr, EnvStr, EnvCount, GetEnv) always works with
Environment of your program.
(Why ParamStr is among
Environment-related routines? Have you tried to call
ParamStr(0)? (But do not compile into memory, when you would
try, becouse it would give strange results))
What can you do with Environment?
IV.4. Procedure FreeEnv;
First, destroy it at all. Return this block of
memory to DOS for any other usage. It is recommended, if you
write a TSR program. But if you destroy Environment of other
process, you have a chance to hang your system.
IV.5. Function GetEnvSize:word;
Ask a size of it (in bytes)
IV.6. Function GetEnvCount:Integer;
Number of lines
IV.7. Function GetEnvSpace:word;
Number of free bytes
IV.8. Function GetEnvStrN(N:Integer):String;
Get line number N
IV.9. Function GetEnvStr(VarName:String):String;
Or value of given variable. Prevouis function returns
string like VARNAME=value, but this - value only.
IV.10.Procedure SetEnvStr(VarName,Value:String);
At last, change value or add new variable, or delete
existing, exactly as DOS command SET does. When SET says
"Out of environment space" this procedure would return 8 in
DOSError
IV.11.Function GetProgName:String;
Get full path to environmemt owner's executable file (if it
is not COMMAND.COM)
Following functions retrieve information not from
Environment, but from PSP
IV.12. Function GetCommandLine:String;
Get command line (whole, not bu pieces like ParamStr)
IV.13. Function CommandLineAddress:Pointer;
Get address of command line, for example for changing it.
(Returned value really has type ^Strinf[127])
IV.14.Function OldInt22H:Pointer;
IV.15.Function OldInt23H:Pointer;
IV.16.Function OldInt24H:Pointer;
Get addresses of termination routine (int 22H),
Ctrl-Break handler (int 23H) and critical error handler,
which were active before start of process, whose environment
is now accessed (for current program they are equial to
SaveIntXX variables in System unit)
V. Dealing with DOS memory manager
You, of course, know, that free memory, which is returned
by MemAvail and can be used by New and GetMem, is not free for
DOS, but is in your program posession. Therefore, if you didn't
limit heap size by $M directuve, you cannot ask dos to perform
anything, which requires some memory. (for example load another
program by Exec). Your attempt would cause only value 8 (Not
enough memory)in DosError.
But I'm miser. I won't limit myself in heap usage and I want
to give memory to DOS only if it really needed. Note, that when
you call child process or DOS Shell, you must save to disk all
what possible, therefore you can free up some memory.
Memory unit of Turbo Vision have procedure SetMemTop for this
purpose. But if you hate Turbo Vision, you can use same
procedure from SWDOS.
V.1. Procedure SetMemTop(MemTop:Pointer);
This is designed only for TP version 6.0 and 7.0, real mode.
In protected mode you don't need it at all, becouse all memory,
which free for you is also free for DPMI. Versions 4.x and 5.x
have another heap manager and shrinking of memory in this
versions is more complicated task. But it is possible. You can
find it in Turbo Professional or Object Professional by Turbo
Power (unit TPDOS/OPDOS, procedure SetBlock).
But SWDOS is designed for newer versions, and I won't waste
my time, trying to understand old heap manager. It is enough,
that I didn't use inline assembler, to allow SWDOS compilation
in old versions.
You can give all free heap space to DOS by
SetMemTop(HeapPtr) and obtain it back by SetMemTop(HeapEnd).
You also may want to use nearly all memory and leave to DOS
only a small piece. For example to open more files using
MaxFiles procedure. Becouse you don't know, how many memory
would be
free when your program would run next time (it is depended of
loaded TSR's, DOS version etc), it is better to get all
availiable memory and then free some amount.
I usially use following sequence:
Dec(LongInt(HeapEnd),$400000);
{decrease heap end segment by $40, i.e 1 Kb}
SetMemTop(HeapEnd);
{Return memory above new HeapEnd to DOS}
But if you have memory, which is free at DOS level, you may
wish to use it yourself. Use:
V.2 Function DOSAlloc(var Size:Longint):Pointer;
to obtain memory block from DOS and:
V.3 Procedure DOSFree(P:Pointer);
To free it. (all memory blocks, allocated by your program,
would be freed when your program is terminated);
Last procedure sometimes returns code 204 (invalid pointer
operation) in DOSError.
It means that you pass incorrect pointer to it (pointer which
have nonzero offset)
Now some words about more fine things, which are availiable
only from DOS 5.0 and higher - about Upper memory blocks (UMB)
You can control their allocation by setting allocation
strategy. (see constants msXXXX in SWDOS.PAS)
You can set allocation strategy by:
V.4. Procedure SetAllocationStrategy(Strategy:Word);
It is strictly recommended to obtain old allocation strategy
before setting your own by:
V.5. Function GetAllocationStrategy:Word;
and restore it before termination, becouse if you set strategy
msFirstFitHighOnly, poor DOS would try to load any subsequent
program into UMB.
But there is another thing - global enabling/disabling of UMB.
I don't know, how it corresponds with prevouis couple of
functions, but there is an interface:
V.6. Function GetUMBLink:Boolean;
V.7. Procedure SetUMBLink(Allow:Boolean);
VI. UpCase International Ltd
I suppose, each programmer, except native English, was very
angry, when he learn, that Upcase function even in localized
Borland Pascal doesn't understand national symbols.
DOS is more clever in this case. If you write in your
CONFIG.SYS COUNTRY=xxx, you teach it do this.
If you would use DOS function to Upcase characters, your
future user from other country wouldn't be angry with you for
strange opinion of your program about capitalizing of letters.
VI.1. Function UpCase(ch:Char):Char;
Replaces function of System unit
VI.2. Function StUpCase(S:String):String;
Upcases whole string (replaces function from
TPString/OPString, which is more clever then standard Upcase,
but not clever enough. For example, it doesn't understand
Russian)
VI.3. Procedure UpcaseStr(Str:PChar);
Uppercases ASCIIZ (null-terminated string). Replaces function
from Strings unit. Version 7.0 only.
ATTENTION! DOS calls, which used by this function were first
documented in DOS 5.0. It seems to work in MS-DOS 3.30, but may
not work in DOSes of another vendors like COMPAQ in versions
prior 5.00. It surely doesn't work in Compaq DOS 3.31.
VII. Temporary files
DOS can create temorary files with unique names.
You may say: why do anything with it? I woudld give my
temporary file name like CLUST$$$$$.$$$ and nobody would have
an idea to overwrite it. But what happens, when somebody would
try to start two copies of your program in different windows of
Windows, or in same shared directory on network?
You can use this possibility by:
VII.1 Procedure AssignTemp(var F;TempDir:String);
This procedure creates, but not opens, (more exact -
immediately closes) temorary file in given directory.
You can open it by usial Rewrite, write something there, Reset
it, read etc.
When you don't need it anymore, erase it after closing or
close by CloseTemp
File can be either text or binary.
VII.2 Procedure CloseTemp(var F:Text);
Simply subsequentual Close(F), Erase(F)
Sometimes you may wish to know name of this temporary file, for
instance to pass it to child process.
Use :
VII.3 Function GetFileName(var F):String;
This function retrieves file name from Text or File variable,
like IDE does when you place such variable into Watch window.
VIII. More about names
There are some useful operations, which can be performed
with file name before opening it. (Substitute default extension
for example). There are no such functions in RTL. You can find
something in Turbo/Object Professional, but first, there are
some bugs there (for example if pathname contain directory with
extension, it wouldn't work correctly), second, not every user
of Turbo Pascal has a licensed copy of TPro or OPro.
VIII.1 Function JustFileName(FileName:String):String;
Removes path, if any from file name
VIII.2 Function JustName(FileName:String):String;
Removes path and extension
VIII.3 Function JustExtension(FileName:String):String;
Returns bare extension
VIII.4 Function JustPathName(FileName:String):String;
Returns bare path (like for pass to ChDir)
if you want to add name to this path, do not forget to check
for trailing'\'
VIII.5 Function DefaultExtension(FileName,Extension:String)
:String;
If file name has no extension, substitutes given
VIII.6 Function ForceExtension(FileName,Extension:String)
:String;
Replaces old extension by given
VIII.7 Function ExpandFileName(FileName,DefaultExt,
DefaultDir:String):String;
Substitutes extension and search file in given list of
directories (separated by ; ). If nothing appropriate, returns
empty string.
IX. Text file handling
Every Turbo Pascal knows, that Text type is not File of
char. Text files are bufferized, they have flexible
input/output technic, which allows to use not only DOS files
and devices, but such things as CRT or Turbo Vision window to
text I/O.
But, so useful procedures as
Seek, FilePos ¿ FileSize unapplicable for them. You can use
FindFirst instead of filesize, but you can do nothing with
positioning of text file. Evidently you cannot position devise
except this device own way like GotoXY. But random access to
DOS text files is very nice thing.
Now get it:
IX.1 procedure TextSeek(var F:Text;Position:Longint);
Positions text file. As all other procedures which deal with
text files, set IOResult in case of error. File must be open
for reading (by Reset)
IX.2 function TextPos(var F:Text):Longint;
Returns current postion in bytes. In case of error, returns -1
Sometimes it is good to write some information into
memory block by writeln and then pass this block, for example
in Turbo Vision TMemo object.
IX.3. procedure AssignMemory(var F:Text;Buffer:Pointer;
BufSize:Word);
Allows to open file situated in memory. You can open it by
usial Reset or Rewrite, but if you want to read from it, you
must supply BufSize useful characters in buffer.
Closed by usial Close, but if buffer is dynamically allocated,
do not forget dispose it. May be CloseLoaded procedure below
would be suitable.
IX.4 Procedure LoadFile(var F:Text; FileName:String);
Loads text file into memort and performs AssignMemory. Very
useful, if you want to have multiple passes, becouse there
wouldn't be any disk I/O, even when you Reset this file.
First Reset performs itself.
Close this file by CloseMemory becouse you don't know size
of buffer, which was allocated for this file.
If file was larger then 64K or not enough free memory,
returns 8 in IOResult and opens file without loading.
IX.5. Procedure CloseLoaded(var F:Text)
Closes file and disposes buffer. Usable not only for files,
opened by LoadFile, but also for files, which has dynamically
allocated buffer, which was set by SetTextBuf.
X. Single Drive systems
If you had worked on sistem with one floppy, you have had
already seen a message
Insert disk for drive ... and strike any key.
There is nothing wrong for simple command-line controlled
programs, but if you have complicated user interface with
carefully maintained screen, you would hate all unwanted
messages.
What can you do to intercert control of this. How can you
check - has drive more than one letter,
which letter is used now,
how can you tell DOS, that drive is now B not A, or vise versa?
X.1. Function IsDriveMappable(Drive:Byte):Boolean;
Recieves drive code (0 - current, 1-A, 2-B etc) and
returns true if more than one letter corresponds this drive
X.2. Function GetDriveLetter(Drive:Byte):char;
Recieves drive code and returns letter of this drive.
You may also use this function to check which drive is
currend. If current drive is C:, GetDriveLetter(0) returns 'C',
even there is nothing to do with changing letters.
X.3. Procedure SetDriveLetter(Drive:char);
You can use this function to tell DOS, that floppy drive is now
used as A: or B:, regardless of its own opinion.
But nothing happen, if you would try to set letter, which
does not really correspond with mappable device.
PROTECTED MODE USER NOTES
SWDOS unit was designed to work in real mode. But there are
no real objections to use some functions in protected mode and
even Windows. (but SWDOS uses DOS unit and there are no
DOS.TPW. Due to this you must change uses DOS to uses WINDOS,
becouse the only accessed thing from this unit is DOSError
variable and FSearch function in ExtentFileName)
You cannot use in Protected mode
1. All memory management routines (but WinAPI unit provides
more suitable routines)
2. All environment-related routines. It can be siginificant
lack, but really only procedures AccessXXXEnv FreeEnv
and getEnvSize are prohibited. If you compute correct
selector for environment block and place it in variable
Environment, you can use other procedures. But it seems
to me, that you must also rewrite GetEnvSize, becouse it
is often called from other routines.